/* eslint-disable prefer-const,no-loss-of-precision */
'use strict'

const test = require('tape')

const big = require('../proto/big')
const fbe = require('../proto/fbe')
const int64 = require('../proto/int64')
const proto = require('../proto/proto')
const prototest = require('../proto/test')
const uuid = require('../proto/uuid')

const Big = big.Big
const Int64 = int64.Int64
const UInt64 = int64.UInt64
const UUID = uuid.UUID

test('Serialization: domain', function (t) {
  // Create a new account with some orders
  let account1 = new proto.Account(1, 'Test', proto.State.good, new proto.Balance('USD', 1000.0), new proto.Balance('EUR', 100.0))
  account1.orders.push(new proto.Order(1, 'EURUSD', proto.OrderSide.buy, proto.OrderType.market, 1.23456, 1000.0))
  account1.orders.push(new proto.Order(2, 'EURUSD', proto.OrderSide.sell, proto.OrderType.limit, 1.0, 100.0))
  account1.orders.push(new proto.Order(3, 'EURUSD', proto.OrderSide.buy, proto.OrderType.stop, 1.5, 10.0))

  // Serialize the account to the FBE stream
  let writer = new proto.AccountModel(new fbe.WriteBuffer())
  t.equal(writer.model.fbeOffset, 4)
  let serialized = writer.serialize(account1)
  t.equal(serialized, writer.buffer.size)
  t.true(writer.verify())
  writer.next(serialized)
  t.equal(writer.model.fbeOffset, (4 + writer.buffer.size))

  // Check the serialized FBE size
  t.equal(writer.buffer.size, 252)

  // Deserialize the account from the FBE stream
  let account2 = new proto.Account()
  let reader = new proto.AccountModel(new fbe.ReadBuffer())
  t.equal(reader.model.fbeOffset, 4)
  reader.attachBuffer(writer.buffer)
  t.true(reader.verify())
  let deserialized = reader.deserialize(account2)
  t.true(deserialized.size, reader.buffer.size)
  reader.next(deserialized.size)
  t.true(reader.model.fbeOffset, (4 + reader.buffer.size))

  t.equal(account2.id, 1)
  t.equal(account2.name, 'Test')
  t.true(account2.state.hasFlags(proto.State.good))
  t.equal(account2.wallet.currency, 'USD')
  t.equal(account2.wallet.amount, 1000.0)
  t.notEqual(account2.asset, undefined)
  t.equal(account2.asset.currency, 'EUR')
  t.equal(account2.asset.amount, 100.0)
  t.equal(account2.orders.length, 3)
  t.equal(account2.orders[0].id, 1)
  t.equal(account2.orders[0].symbol, 'EURUSD')
  t.true(account2.orders[0].side.eq(proto.OrderSide.buy))
  t.true(account2.orders[0].type.eq(proto.OrderType.market))
  t.equal(account2.orders[0].price, 1.23456)
  t.equal(account2.orders[0].volume, 1000.0)
  t.equal(account2.orders[1].id, 2)
  t.equal(account2.orders[1].symbol, 'EURUSD')
  t.true(account2.orders[1].side.eq(proto.OrderSide.sell))
  t.true(account2.orders[1].type.eq(proto.OrderType.limit))
  t.equal(account2.orders[1].price, 1.0)
  t.equal(account2.orders[1].volume, 100.0)
  t.equal(account2.orders[2].id, 3)
  t.equal(account2.orders[2].symbol, 'EURUSD')
  t.true(account2.orders[2].side.eq(proto.OrderSide.buy))
  t.true(account2.orders[2].type.eq(proto.OrderType.stop))
  t.equal(account2.orders[2].price, 1.5)
  t.equal(account2.orders[2].volume, 10.0)
  t.end()
})

test('Serialization: struct simple', function (t) {
  // Create a new struct
  let struct1 = new prototest.StructSimple()

  // Serialize the struct to the FBE stream
  let writer = new prototest.StructSimpleModel(new fbe.WriteBuffer())
  t.equal(writer.model.fbeType, 110)
  t.equal(writer.model.fbeOffset, 4)
  let serialized = writer.serialize(struct1)
  t.equal(serialized, writer.buffer.size)
  t.true(writer.verify())
  writer.next(serialized)
  t.equal(writer.model.fbeOffset, (4 + writer.buffer.size))

  // Check the serialized FBE size
  t.equal(writer.buffer.size, 392)

  // Deserialize the struct from the FBE stream
  let struct2 = new prototest.StructSimple()
  let reader = new prototest.StructSimpleModel(new fbe.ReadBuffer())
  t.equal(reader.model.fbeType, 110)
  t.equal(reader.model.fbeOffset, 4)
  reader.attachBuffer(writer.buffer)
  t.true(reader.verify())
  let deserialized = reader.deserialize(struct2)
  t.true(deserialized.size, reader.buffer.size)
  reader.next(deserialized.size)
  t.true(reader.model.fbeOffset, (4 + reader.buffer.size))

  t.equal(struct2.f1, false)
  t.equal(struct2.f2, true)
  t.equal(struct2.f3, 0)
  t.equal(struct2.f4, 0xFF)
  t.equal(struct2.f5, '\0')
  t.equal(struct2.f6, '!')
  t.equal(struct2.f7, String.fromCharCode(0))
  t.equal(struct2.f8, String.fromCharCode(0x0444))
  t.equal(struct2.f9, 0)
  t.equal(struct2.f10, 127)
  t.equal(struct2.f11, 0)
  t.equal(struct2.f12, 0xFF)
  t.equal(struct2.f13, 0)
  t.equal(struct2.f14, 32767)
  t.equal(struct2.f15, 0)
  t.equal(struct2.f16, 0xFFFF)
  t.equal(struct2.f17, 0)
  t.equal(struct2.f18, 2147483647)
  t.equal(struct2.f19, 0)
  t.equal(struct2.f20, 0xFFFFFFFF)
  t.true(struct2.f21.eq(Int64.fromNumber(0)))
  t.true(struct2.f22.eq(Int64.fromNumber(9223372036854775807)))
  t.true(struct2.f23.eq(UInt64.fromNumber(0)))
  t.true(struct2.f24.eq(UInt64.fromNumber(0xFFFFFFFFFFFFFFFF)))
  t.equal(struct2.f25, 0.0)
  t.true(Math.abs(struct2.f26 - 123.456) < 0.0001)
  t.equal(struct2.f27, 0.0)
  t.true(Math.abs(struct2.f28 - -123.567e+123) < 1e+123)
  t.true(struct2.f29.eq(new Big('0')))
  t.true(struct2.f30.eq(new Big('123456.123456')))
  t.equal(struct2.f31, '')
  t.equal(struct2.f32, 'Initial string!')
  t.equal(struct2.f33.getTime(), new Date(Date.UTC(1970, 0, 1)).getTime())
  t.equal(struct2.f34.getTime(), new Date(Date.UTC(1970, 0, 1)).getTime())
  t.true(struct2.f35.getTime() > new Date(Date.UTC(2018, 1, 1)).getTime())
  t.true(struct2.f36.eq(new UUID()))
  t.false(struct2.f37.eq(new UUID()))
  t.true(struct2.f38.eq(new UUID('123e4567-e89b-12d3-a456-426655440000')))

  t.equal(struct2.f1, struct1.f1)
  t.equal(struct2.f2, struct1.f2)
  t.equal(struct2.f3, struct1.f3)
  t.equal(struct2.f4, struct1.f4)
  t.equal(struct2.f5, struct1.f5)
  t.equal(struct2.f6, struct1.f6)
  t.equal(struct2.f7, struct1.f7)
  t.equal(struct2.f8, struct1.f8)
  t.equal(struct2.f9, struct1.f9)
  t.equal(struct2.f10, struct1.f10)
  t.equal(struct2.f11, struct1.f11)
  t.equal(struct2.f12, struct1.f12)
  t.equal(struct2.f13, struct1.f13)
  t.equal(struct2.f14, struct1.f14)
  t.equal(struct2.f15, struct1.f15)
  t.equal(struct2.f16, struct1.f16)
  t.equal(struct2.f17, struct1.f17)
  t.equal(struct2.f18, struct1.f18)
  t.equal(struct2.f19, struct1.f19)
  t.equal(struct2.f20, struct1.f20)
  t.true(struct2.f21.eq(struct1.f21))
  t.true(struct2.f22.eq(struct1.f22))
  t.true(struct2.f23.eq(struct1.f23))
  t.true(struct2.f24.eq(struct1.f24))
  t.equal(struct2.f25, struct1.f25)
  t.true(Math.abs(struct2.f26 - struct1.f26) < 0.0001)
  t.equal(struct2.f27, struct1.f27)
  t.true(Math.abs(struct2.f28 - struct1.f28) < 1e+123)
  t.true(struct2.f29.eq(struct1.f29))
  t.true(struct2.f29.eq(struct1.f29))
  t.equal(struct2.f31, struct1.f31)
  t.equal(struct2.f32, struct1.f32)
  t.equal(struct2.f33.getTime(), struct1.f33.getTime())
  t.equal(struct2.f34.getTime(), struct1.f34.getTime())
  t.equal(struct2.f35.getTime(), struct1.f35.getTime())
  t.true(struct2.f36.eq(struct1.f36))
  t.true(struct2.f37.eq(struct1.f37))
  t.true(struct2.f38.eq(struct1.f38))
  t.true(struct2.f39.eq(struct1.f39))
  t.true(struct2.f40.eq(struct1.f40))
  t.end()
})

test('Serialization: struct optional', function (t) {
  // Create a new struct
  let struct1 = new prototest.StructOptional()

  // Serialize the struct to the FBE stream
  let writer = new prototest.StructOptionalModel(new fbe.WriteBuffer())
  t.equal(writer.model.fbeType, 111)
  t.equal(writer.model.fbeOffset, 4)
  let serialized = writer.serialize(struct1)
  t.equal(serialized, writer.buffer.size)
  t.true(writer.verify())
  writer.next(serialized)
  t.equal(writer.model.fbeOffset, (4 + writer.buffer.size))

  // Check the serialized FBE size
  t.equal(writer.buffer.size, 834)

  // Deserialize the struct from the FBE stream
  let struct2 = new prototest.StructOptional()
  let reader = new prototest.StructOptionalModel(new fbe.ReadBuffer())
  t.equal(reader.model.fbeType, 111)
  t.equal(reader.model.fbeOffset, 4)
  reader.attachBuffer(writer.buffer)
  t.true(reader.verify())
  let deserialized = reader.deserialize(struct2)
  t.true(deserialized.size, reader.buffer.size)
  reader.next(deserialized.size)
  t.true(reader.model.fbeOffset, (4 + reader.buffer.size))

  t.equal(struct2.f1, false)
  t.equal(struct2.f2, true)
  t.equal(struct2.f3, 0)
  t.equal(struct2.f4, 0xFF)
  t.equal(struct2.f5, '\0')
  t.equal(struct2.f6, '!')
  t.equal(struct2.f7, String.fromCharCode(0))
  t.equal(struct2.f8, String.fromCharCode(0x0444))
  t.equal(struct2.f9, 0)
  t.equal(struct2.f10, 127)
  t.equal(struct2.f11, 0)
  t.equal(struct2.f12, 0xFF)
  t.equal(struct2.f13, 0)
  t.equal(struct2.f14, 32767)
  t.equal(struct2.f15, 0)
  t.equal(struct2.f16, 0xFFFF)
  t.equal(struct2.f17, 0)
  t.equal(struct2.f18, 2147483647)
  t.equal(struct2.f19, 0)
  t.equal(struct2.f20, 0xFFFFFFFF)
  t.true(struct2.f21.eq(Int64.fromNumber(0)))
  t.true(struct2.f22.eq(Int64.fromNumber(9223372036854775807)))
  t.true(struct2.f23.eq(UInt64.fromNumber(0)))
  t.true(struct2.f24.eq(UInt64.fromNumber(0xFFFFFFFFFFFFFFFF)))
  t.equal(struct2.f25, 0.0)
  t.true(Math.abs(struct2.f26 - 123.456) < 0.0001)
  t.equal(struct2.f27, 0.0)
  t.true(Math.abs(struct2.f28 - -123.567e+123) < 1e+123)
  t.true(struct2.f29.eq(new Big('0')))
  t.true(struct2.f30.eq(new Big('123456.123456')))
  t.equal(struct2.f31, '')
  t.equal(struct2.f32, 'Initial string!')
  t.equal(struct2.f33.getTime(), new Date(Date.UTC(1970, 0, 1)).getTime())
  t.equal(struct2.f34.getTime(), new Date(Date.UTC(1970, 0, 1)).getTime())
  t.true(struct2.f35.getTime() > new Date(Date.UTC(2018, 1, 1)).getTime())
  t.true(struct2.f36.eq(new UUID()))
  t.false(struct2.f37.eq(new UUID()))
  t.true(struct2.f38.eq(new UUID('123e4567-e89b-12d3-a456-426655440000')))

  t.equal(struct2.f100, undefined)
  t.notEqual(struct2.f101, null)
  t.equal(struct2.f101, true)
  t.equal(struct2.f102, undefined)
  t.equal(struct2.f103, undefined)
  t.notEqual(struct2.f104, null)
  t.equal(struct2.f104, 0xFF)
  t.equal(struct2.f105, undefined)
  t.equal(struct2.f106, undefined)
  t.notEqual(struct2.f107, null)
  t.equal(struct2.f107, '!')
  t.equal(struct2.f108, undefined)
  t.equal(struct2.f109, undefined)
  t.notEqual(struct2.f110, null)
  t.equal(struct2.f110, String.fromCharCode(0x0444))
  t.equal(struct2.f111, undefined)
  t.equal(struct2.f112, undefined)
  t.notEqual(struct2.f113, null)
  t.equal(struct2.f113, 127)
  t.equal(struct2.f114, undefined)
  t.equal(struct2.f115, undefined)
  t.notEqual(struct2.f116, null)
  t.equal(struct2.f116, 0xFF)
  t.equal(struct2.f117, undefined)
  t.equal(struct2.f118, undefined)
  t.notEqual(struct2.f119, null)
  t.equal(struct2.f119, 32767)
  t.equal(struct2.f120, undefined)
  t.equal(struct2.f121, undefined)
  t.notEqual(struct2.f122, null)
  t.equal(struct2.f122, 0xFFFF)
  t.equal(struct2.f123, undefined)
  t.equal(struct2.f124, undefined)
  t.notEqual(struct2.f125, null)
  t.equal(struct2.f125, 2147483647)
  t.equal(struct2.f126, undefined)
  t.equal(struct2.f127, undefined)
  t.notEqual(struct2.f128, null)
  t.equal(struct2.f128, 0xFFFFFFFF)
  t.equal(struct2.f129, undefined)
  t.equal(struct2.f130, undefined)
  t.notEqual(struct2.f131, null)
  t.true(struct2.f131.eq(Int64.fromNumber(9223372036854775807)))
  t.equal(struct2.f132, undefined)
  t.equal(struct2.f133, undefined)
  t.notEqual(struct2.f134, null)
  t.true(struct2.f134.eq(UInt64.fromNumber(0xFFFFFFFFFFFFFFFF)))
  t.equal(struct2.f135, undefined)
  t.equal(struct2.f136, undefined)
  t.notEqual(struct2.f137, null)
  t.true(Math.abs(struct2.f137 - 123.456) < 0.0001)
  t.equal(struct2.f138, undefined)
  t.equal(struct2.f139, undefined)
  t.notEqual(struct2.f140, null)
  t.true(Math.abs(struct2.f140 - -123.567e+123) < 1e+123)
  t.equal(struct2.f141, undefined)
  t.equal(struct2.f142, undefined)
  t.notEqual(struct2.f143, null)
  t.true(struct2.f143.eq(new Big('123456.123456')))
  t.equal(struct2.f144, undefined)
  t.equal(struct2.f145, undefined)
  t.notEqual(struct2.f146, null)
  t.equal(struct2.f146, 'Initial string!')
  t.equal(struct2.f147, undefined)
  t.equal(struct2.f148, undefined)
  t.notEqual(struct2.f149, null)
  t.true(struct2.f149.getTime() > new Date(Date.UTC(2018, 1, 1)).getTime())
  t.equal(struct2.f150, undefined)
  t.equal(struct2.f151, undefined)
  t.notEqual(struct2.f152, null)
  t.true(struct2.f152.eq(new UUID('123e4567-e89b-12d3-a456-426655440000')))
  t.equal(struct2.f153, undefined)
  t.equal(struct2.f154, undefined)
  t.equal(struct2.f155, undefined)
  t.equal(struct2.f156, undefined)
  t.equal(struct2.f157, undefined)
  t.equal(struct2.f158, undefined)
  t.equal(struct2.f159, undefined)
  t.equal(struct2.f160, undefined)
  t.equal(struct2.f161, undefined)
  t.equal(struct2.f162, undefined)
  t.equal(struct2.f163, undefined)
  t.equal(struct2.f164, undefined)
  t.equal(struct2.f165, undefined)

  t.equal(struct2.f1, struct1.f1)
  t.equal(struct2.f2, struct1.f2)
  t.equal(struct2.f3, struct1.f3)
  t.equal(struct2.f4, struct1.f4)
  t.equal(struct2.f5, struct1.f5)
  t.equal(struct2.f6, struct1.f6)
  t.equal(struct2.f7, struct1.f7)
  t.equal(struct2.f8, struct1.f8)
  t.equal(struct2.f9, struct1.f9)
  t.equal(struct2.f10, struct1.f10)
  t.equal(struct2.f11, struct1.f11)
  t.equal(struct2.f12, struct1.f12)
  t.equal(struct2.f13, struct1.f13)
  t.equal(struct2.f14, struct1.f14)
  t.equal(struct2.f15, struct1.f15)
  t.equal(struct2.f16, struct1.f16)
  t.equal(struct2.f17, struct1.f17)
  t.equal(struct2.f18, struct1.f18)
  t.equal(struct2.f19, struct1.f19)
  t.equal(struct2.f20, struct1.f20)
  t.true(struct2.f21.eq(struct1.f21))
  t.true(struct2.f22.eq(struct1.f22))
  t.true(struct2.f23.eq(struct1.f23))
  t.true(struct2.f24.eq(struct1.f24))
  t.equal(struct2.f25, struct1.f25)
  t.true(Math.abs(struct2.f26 - struct1.f26) < 0.0001)
  t.equal(struct2.f27, struct1.f27)
  t.true(Math.abs(struct2.f28 - struct1.f28) < 1e+123)
  t.true(struct2.f29.eq(struct1.f29))
  t.true(struct2.f30.eq(struct1.f30))
  t.equal(struct2.f31, struct1.f31)
  t.equal(struct2.f32, struct1.f32)
  t.equal(struct2.f33.getTime(), struct1.f33.getTime())
  t.equal(struct2.f34.getTime(), struct1.f34.getTime())
  t.equal(struct2.f35.getTime(), struct1.f35.getTime())
  t.true(struct2.f36.eq(struct1.f36))
  t.true(struct2.f37.eq(struct1.f37))
  t.true(struct2.f38.eq(struct1.f38))
  t.true(struct2.f39.eq(struct1.f39))
  t.true(struct2.f40.eq(struct1.f40))

  t.equal(struct2.f100, struct1.f100)
  t.equal(struct2.f101, struct1.f101)
  t.equal(struct2.f102, struct1.f102)
  t.equal(struct2.f103, struct1.f103)
  t.equal(struct2.f104, struct1.f104)
  t.equal(struct2.f105, struct1.f105)
  t.equal(struct2.f106, struct1.f106)
  t.equal(struct2.f107, struct1.f107)
  t.equal(struct2.f108, struct1.f108)
  t.equal(struct2.f109, struct1.f109)
  t.equal(struct2.f110, struct1.f110)
  t.equal(struct2.f111, struct1.f111)
  t.equal(struct2.f112, struct1.f112)
  t.equal(struct2.f113, struct1.f113)
  t.equal(struct2.f114, struct1.f114)
  t.equal(struct2.f115, struct1.f115)
  t.equal(struct2.f116, struct1.f116)
  t.equal(struct2.f117, struct1.f117)
  t.equal(struct2.f118, struct1.f118)
  t.equal(struct2.f119, struct1.f119)
  t.equal(struct2.f120, struct1.f120)
  t.equal(struct2.f121, struct1.f121)
  t.equal(struct2.f122, struct1.f122)
  t.equal(struct2.f123, struct1.f123)
  t.equal(struct2.f124, struct1.f124)
  t.equal(struct2.f125, struct1.f125)
  t.equal(struct2.f126, struct1.f126)
  t.equal(struct2.f127, struct1.f127)
  t.equal(struct2.f128, struct1.f128)
  t.equal(struct2.f129, struct1.f129)
  t.equal(struct2.f130, struct1.f130)
  t.true(struct2.f131.eq(struct1.f131))
  t.equal(struct2.f132, struct1.f132)
  t.equal(struct2.f133, struct1.f133)
  t.true(struct2.f134.eq(struct1.f134))
  t.equal(struct2.f135, struct1.f135)
  t.equal(struct2.f136, struct1.f136)
  t.true(Math.abs(struct2.f137 - struct1.f137) < 0.0001)
  t.equal(struct2.f138, struct1.f138)
  t.equal(struct2.f139, struct1.f139)
  t.true(Math.abs(struct2.f140 - struct1.f140) < 1e+123)
  t.equal(struct2.f141, struct1.f141)
  t.equal(struct2.f142, struct1.f142)
  t.true(struct2.f143.eq(struct1.f143))
  t.equal(struct2.f144, struct1.f144)
  t.equal(struct2.f145, struct1.f145)
  t.equal(struct2.f146, struct1.f146)
  t.equal(struct2.f147, struct1.f147)
  t.equal(struct2.f148, struct1.f148)
  t.equal(struct2.f149.getTime(), struct1.f149.getTime())
  t.equal(struct2.f150, struct1.f150)
  t.equal(struct2.f151, struct1.f151)
  t.true(struct2.f152.eq(struct1.f152))
  t.equal(struct2.f153, struct1.f153)
  t.equal(struct2.f154, struct1.f154)
  t.equal(struct2.f155, struct1.f155)
  t.equal(struct2.f156, struct1.f156)
  t.equal(struct2.f157, struct1.f157)
  t.end()
})

test('Serialization: struct nested', function (t) {
  // Create a new struct
  let struct1 = new prototest.StructNested()

  // Serialize the struct to the FBE stream
  let writer = new prototest.StructNestedModel(new fbe.WriteBuffer())
  t.equal(writer.model.fbeType, 112)
  t.equal(writer.model.fbeOffset, 4)
  let serialized = writer.serialize(struct1)
  t.equal(serialized, writer.buffer.size)
  t.true(writer.verify())
  writer.next(serialized)
  t.equal(writer.model.fbeOffset, (4 + writer.buffer.size))

  // Check the serialized FBE size
  t.equal(writer.buffer.size, 2099)

  // Deserialize the struct from the FBE stream
  let struct2 = new prototest.StructNested()
  let reader = new prototest.StructNestedModel(new fbe.ReadBuffer())
  t.equal(reader.model.fbeType, 112)
  t.equal(reader.model.fbeOffset, 4)
  reader.attachBuffer(writer.buffer)
  t.true(reader.verify())
  let deserialized = reader.deserialize(struct2)
  t.true(deserialized.size, reader.buffer.size)
  reader.next(deserialized.size)
  t.true(reader.model.fbeOffset, (4 + reader.buffer.size))

  t.equal(struct2.f1, false)
  t.equal(struct2.f2, true)
  t.equal(struct2.f3, 0)
  t.equal(struct2.f4, 0xFF)
  t.equal(struct2.f5, '\0')
  t.equal(struct2.f6, '!')
  t.equal(struct2.f7, String.fromCharCode(0))
  t.equal(struct2.f8, String.fromCharCode(0x0444))
  t.equal(struct2.f9, 0)
  t.equal(struct2.f10, 127)
  t.equal(struct2.f11, 0)
  t.equal(struct2.f12, 0xFF)
  t.equal(struct2.f13, 0)
  t.equal(struct2.f14, 32767)
  t.equal(struct2.f15, 0)
  t.equal(struct2.f16, 0xFFFF)
  t.equal(struct2.f17, 0)
  t.equal(struct2.f18, 2147483647)
  t.equal(struct2.f19, 0)
  t.equal(struct2.f20, 0xFFFFFFFF)
  t.true(struct2.f21.eq(Int64.fromNumber(0)))
  t.true(struct2.f22.eq(Int64.fromNumber(9223372036854775807)))
  t.true(struct2.f23.eq(UInt64.fromNumber(0)))
  t.true(struct2.f24.eq(UInt64.fromNumber(0xFFFFFFFFFFFFFFFF)))
  t.equal(struct2.f25, 0.0)
  t.true(Math.abs(struct2.f26 - 123.456) < 0.0001)
  t.equal(struct2.f27, 0.0)
  t.true(Math.abs(struct2.f28 - -123.567e+123) < 1e+123)
  t.true(struct2.f29.eq(new Big('0')))
  t.true(struct2.f30.eq(new Big('123456.123456')))
  t.equal(struct2.f31, '')
  t.equal(struct2.f32, 'Initial string!')
  t.equal(struct2.f33.getTime(), new Date(Date.UTC(1970, 0, 1)).getTime())
  t.equal(struct2.f34.getTime(), new Date(Date.UTC(1970, 0, 1)).getTime())
  t.true(struct2.f35.getTime() > new Date(Date.UTC(2018, 1, 1)).getTime())
  t.true(struct2.f36.eq(new UUID()))
  t.false(struct2.f37.eq(new UUID()))
  t.true(struct2.f38.eq(new UUID('123e4567-e89b-12d3-a456-426655440000')))

  t.equal(struct2.f100, undefined)
  t.notEqual(struct2.f101, null)
  t.equal(struct2.f101, true)
  t.equal(struct2.f102, undefined)
  t.equal(struct2.f103, undefined)
  t.notEqual(struct2.f104, null)
  t.equal(struct2.f104, 0xFF)
  t.equal(struct2.f105, undefined)
  t.equal(struct2.f106, undefined)
  t.notEqual(struct2.f107, null)
  t.equal(struct2.f107, '!')
  t.equal(struct2.f108, undefined)
  t.equal(struct2.f109, undefined)
  t.notEqual(struct2.f110, null)
  t.equal(struct2.f110, String.fromCharCode(0x0444))
  t.equal(struct2.f111, undefined)
  t.equal(struct2.f112, undefined)
  t.notEqual(struct2.f113, null)
  t.equal(struct2.f113, 127)
  t.equal(struct2.f114, undefined)
  t.equal(struct2.f115, undefined)
  t.notEqual(struct2.f116, null)
  t.equal(struct2.f116, 0xFF)
  t.equal(struct2.f117, undefined)
  t.equal(struct2.f118, undefined)
  t.notEqual(struct2.f119, null)
  t.equal(struct2.f119, 32767)
  t.equal(struct2.f120, undefined)
  t.equal(struct2.f121, undefined)
  t.notEqual(struct2.f122, null)
  t.equal(struct2.f122, 0xFFFF)
  t.equal(struct2.f123, undefined)
  t.equal(struct2.f124, undefined)
  t.notEqual(struct2.f125, null)
  t.equal(struct2.f125, 2147483647)
  t.equal(struct2.f126, undefined)
  t.equal(struct2.f127, undefined)
  t.notEqual(struct2.f128, null)
  t.equal(struct2.f128, 0xFFFFFFFF)
  t.equal(struct2.f129, undefined)
  t.equal(struct2.f130, undefined)
  t.notEqual(struct2.f131, null)
  t.true(struct2.f131.eq(Int64.fromNumber(9223372036854775807)))
  t.equal(struct2.f132, undefined)
  t.equal(struct2.f133, undefined)
  t.notEqual(struct2.f134, null)
  t.true(struct2.f134.eq(UInt64.fromNumber(0xFFFFFFFFFFFFFFFF)))
  t.equal(struct2.f135, undefined)
  t.equal(struct2.f136, undefined)
  t.notEqual(struct2.f137, null)
  t.true(Math.abs(struct2.f137 - 123.456) < 0.0001)
  t.equal(struct2.f138, undefined)
  t.equal(struct2.f139, undefined)
  t.notEqual(struct2.f140, null)
  t.true(Math.abs(struct2.f140 - -123.567e+123) < 1e+123)
  t.equal(struct2.f141, undefined)
  t.equal(struct2.f142, undefined)
  t.notEqual(struct2.f143, null)
  t.true(struct2.f143.eq(new Big('123456.123456')))
  t.equal(struct2.f144, undefined)
  t.equal(struct2.f145, undefined)
  t.notEqual(struct2.f146, null)
  t.equal(struct2.f146, 'Initial string!')
  t.equal(struct2.f147, undefined)
  t.equal(struct2.f148, undefined)
  t.notEqual(struct2.f149, null)
  t.true(struct2.f149.getTime() > new Date(Date.UTC(2018, 1, 1)).getTime())
  t.equal(struct2.f150, undefined)
  t.equal(struct2.f151, undefined)
  t.notEqual(struct2.f152, null)
  t.true(struct2.f152.eq(new UUID('123e4567-e89b-12d3-a456-426655440000')))
  t.equal(struct2.f153, undefined)
  t.equal(struct2.f154, undefined)
  t.equal(struct2.f155, undefined)
  t.equal(struct2.f156, undefined)
  t.equal(struct2.f157, undefined)
  t.equal(struct2.f158, undefined)
  t.equal(struct2.f159, undefined)
  t.equal(struct2.f160, undefined)
  t.equal(struct2.f161, undefined)
  t.equal(struct2.f162, undefined)
  t.equal(struct2.f163, undefined)
  t.equal(struct2.f164, undefined)
  t.equal(struct2.f165, undefined)

  t.true(struct2.f1000.eq(prototest.EnumSimple.ENUM_VALUE_0))
  t.equal(struct2.f1001, undefined)
  t.true(struct2.f1002.eq(prototest.EnumTyped.ENUM_VALUE_2))
  t.equal(struct2.f1003, undefined)
  t.true(struct2.f1004.eq(prototest.FlagsSimple.FLAG_VALUE_0))
  t.equal(struct2.f1005, undefined)
  t.true(struct2.f1006.hasFlags(prototest.FlagsTyped.FLAG_VALUE_2 | prototest.FlagsTyped.FLAG_VALUE_4 | prototest.FlagsTyped.FLAG_VALUE_6))
  t.equal(struct2.f1007, undefined)
  t.equal(struct2.f1009, undefined)
  t.equal(struct2.f1011, undefined)

  t.equal(struct2.f1, struct1.f1)
  t.equal(struct2.f2, struct1.f2)
  t.equal(struct2.f3, struct1.f3)
  t.equal(struct2.f4, struct1.f4)
  t.equal(struct2.f5, struct1.f5)
  t.equal(struct2.f6, struct1.f6)
  t.equal(struct2.f7, struct1.f7)
  t.equal(struct2.f8, struct1.f8)
  t.equal(struct2.f9, struct1.f9)
  t.equal(struct2.f10, struct1.f10)
  t.equal(struct2.f11, struct1.f11)
  t.equal(struct2.f12, struct1.f12)
  t.equal(struct2.f13, struct1.f13)
  t.equal(struct2.f14, struct1.f14)
  t.equal(struct2.f15, struct1.f15)
  t.equal(struct2.f16, struct1.f16)
  t.equal(struct2.f17, struct1.f17)
  t.equal(struct2.f18, struct1.f18)
  t.equal(struct2.f19, struct1.f19)
  t.equal(struct2.f20, struct1.f20)
  t.true(struct2.f21.eq(struct1.f21))
  t.true(struct2.f22.eq(struct1.f22))
  t.true(struct2.f23.eq(struct1.f23))
  t.true(struct2.f24.eq(struct1.f24))
  t.equal(struct2.f25, struct1.f25)
  t.true(Math.abs(struct2.f26 - struct1.f26) < 0.0001)
  t.equal(struct2.f27, struct1.f27)
  t.true(Math.abs(struct2.f28 - struct1.f28) < 1e+123)
  t.true(struct2.f29.eq(struct1.f29))
  t.true(struct2.f29.eq(struct1.f29))
  t.equal(struct2.f31, struct1.f31)
  t.equal(struct2.f32, struct1.f32)
  t.equal(struct2.f33.getTime(), struct1.f33.getTime())
  t.equal(struct2.f34.getTime(), struct1.f34.getTime())
  t.equal(struct2.f35.getTime(), struct1.f35.getTime())
  t.true(struct2.f36.eq(struct1.f36))
  t.true(struct2.f37.eq(struct1.f37))
  t.true(struct2.f38.eq(struct1.f38))
  t.true(struct2.f39.eq(struct1.f39))
  t.true(struct2.f40.eq(struct1.f40))

  t.equal(struct2.f100, struct1.f100)
  t.equal(struct2.f101, struct1.f101)
  t.equal(struct2.f102, struct1.f102)
  t.equal(struct2.f103, struct1.f103)
  t.equal(struct2.f104, struct1.f104)
  t.equal(struct2.f105, struct1.f105)
  t.equal(struct2.f106, struct1.f106)
  t.equal(struct2.f107, struct1.f107)
  t.equal(struct2.f108, struct1.f108)
  t.equal(struct2.f109, struct1.f109)
  t.equal(struct2.f110, struct1.f110)
  t.equal(struct2.f111, struct1.f111)
  t.equal(struct2.f112, struct1.f112)
  t.equal(struct2.f113, struct1.f113)
  t.equal(struct2.f114, struct1.f114)
  t.equal(struct2.f115, struct1.f115)
  t.equal(struct2.f116, struct1.f116)
  t.equal(struct2.f117, struct1.f117)
  t.equal(struct2.f118, struct1.f118)
  t.equal(struct2.f119, struct1.f119)
  t.equal(struct2.f120, struct1.f120)
  t.equal(struct2.f121, struct1.f121)
  t.equal(struct2.f122, struct1.f122)
  t.equal(struct2.f123, struct1.f123)
  t.equal(struct2.f124, struct1.f124)
  t.equal(struct2.f125, struct1.f125)
  t.equal(struct2.f126, struct1.f126)
  t.equal(struct2.f127, struct1.f127)
  t.equal(struct2.f128, struct1.f128)
  t.equal(struct2.f129, struct1.f129)
  t.equal(struct2.f130, struct1.f130)
  t.true(struct2.f131.eq(struct1.f131))
  t.equal(struct2.f132, struct1.f132)
  t.equal(struct2.f133, struct1.f133)
  t.true(struct2.f134.eq(struct1.f134))
  t.equal(struct2.f135, struct1.f135)
  t.equal(struct2.f136, struct1.f136)
  t.true(Math.abs(struct2.f137 - struct1.f137) < 0.0001)
  t.equal(struct2.f138, struct1.f138)
  t.equal(struct2.f139, struct1.f139)
  t.true(Math.abs(struct2.f140 - struct1.f140) < 1e+123)
  t.equal(struct2.f141, struct1.f141)
  t.equal(struct2.f142, struct1.f142)
  t.true(struct2.f143.eq(struct1.f143))
  t.equal(struct2.f144, struct1.f144)
  t.equal(struct2.f145, struct1.f145)
  t.equal(struct2.f146, struct1.f146)
  t.equal(struct2.f147, struct1.f147)
  t.equal(struct2.f148, struct1.f148)
  t.equal(struct2.f149.getTime(), struct1.f149.getTime())
  t.equal(struct2.f150, struct1.f150)
  t.equal(struct2.f151, struct1.f151)
  t.true(struct2.f152.eq(struct1.f152))
  t.equal(struct2.f153, struct1.f153)
  t.equal(struct2.f154, struct1.f154)
  t.equal(struct2.f155, struct1.f155)
  t.equal(struct2.f156, struct1.f156)
  t.equal(struct2.f157, struct1.f157)

  t.true(struct2.f1000.eq(struct1.f1000))
  t.equal(struct2.f1001, struct1.f1001)
  t.true(struct2.f1002.eq(struct1.f1002))
  t.equal(struct2.f1003, struct1.f1003)
  t.true(struct2.f1004.eq(struct1.f1004))
  t.equal(struct2.f1005, struct1.f1005)
  t.true(struct2.f1006.eq(struct1.f1006))
  t.equal(struct2.f1007, struct1.f1007)
  t.end()
})

test('Serialization: struct bytes', function (t) {
  // Create a new struct
  let struct1 = new prototest.StructBytes()
  // noinspection JSUnresolvedFunction
  struct1.f1 = Uint8Array.from('ABC', x => x.charCodeAt(0))
  // noinspection JSUnresolvedFunction
  struct1.f2 = Uint8Array.from('test', x => x.charCodeAt(0))

  // Serialize the struct to the FBE stream
  let writer = new prototest.StructBytesModel(new fbe.WriteBuffer())
  t.equal(writer.model.fbeType, 120)
  t.equal(writer.model.fbeOffset, 4)
  let serialized = writer.serialize(struct1)
  t.equal(serialized, writer.buffer.size)
  t.true(writer.verify())
  writer.next(serialized)
  t.equal(writer.model.fbeOffset, (4 + writer.buffer.size))

  // Check the serialized FBE size
  t.equal(writer.buffer.size, 49)

  // Deserialize the struct from the FBE stream
  let struct2 = new prototest.StructBytes()
  let reader = new prototest.StructBytesModel(new fbe.ReadBuffer())
  t.equal(reader.model.fbeType, 120)
  t.equal(reader.model.fbeOffset, 4)
  reader.attachBuffer(writer.buffer)
  t.true(reader.verify())
  let deserialized = reader.deserialize(struct2)
  t.true(deserialized.size, reader.buffer.size)
  reader.next(deserialized.size)
  t.true(reader.model.fbeOffset, (4 + reader.buffer.size))

  t.equal(struct2.f1.length, 3)
  t.equal(struct2.f1[0], 'A'.charCodeAt(0))
  t.equal(struct2.f1[1], 'B'.charCodeAt(0))
  t.equal(struct2.f1[2], 'C'.charCodeAt(0))
  t.notEqual(struct2.f2, undefined)
  t.equal(struct2.f2.length, 4)
  t.equal(struct2.f2[0], 't'.charCodeAt(0))
  t.equal(struct2.f2[1], 'e'.charCodeAt(0))
  t.equal(struct2.f2[2], 's'.charCodeAt(0))
  t.equal(struct2.f2[3], 't'.charCodeAt(0))
  t.equal(struct2.f3, undefined)
  t.end()
})

test('Serialization: struct array', function (t) {
  // Create a new struct
  let struct1 = new prototest.StructArray()
  struct1.f1[0] = 48
  struct1.f1[1] = 65
  struct1.f2[0] = 97
  struct1.f2[1] = undefined
  struct1.f3[0] = Array.from('000', x => x.charCodeAt(0))
  struct1.f3[1] = Array.from('AAA', x => x.charCodeAt(0))
  struct1.f4[0] = Array.from('aaa', x => x.charCodeAt(0))
  struct1.f4[1] = undefined
  struct1.f5[0] = prototest.EnumSimple.ENUM_VALUE_1
  struct1.f5[1] = prototest.EnumSimple.ENUM_VALUE_2
  struct1.f6[0] = prototest.EnumSimple.ENUM_VALUE_1
  struct1.f6[1] = undefined
  struct1.f7[0] = prototest.FlagsSimple.fromFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2)
  struct1.f7[1] = prototest.FlagsSimple.fromFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2 | prototest.FlagsSimple.FLAG_VALUE_3)
  struct1.f8[0] = prototest.FlagsSimple.fromFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2)
  struct1.f8[1] = undefined
  struct1.f9[0] = new prototest.StructSimple()
  struct1.f9[1] = new prototest.StructSimple()
  struct1.f10[0] = new prototest.StructSimple()
  struct1.f10[1] = undefined

  // Serialize the struct to the FBE stream
  let writer = new prototest.StructArrayModel(new fbe.WriteBuffer())
  t.equal(writer.model.fbeType, 125)
  t.equal(writer.model.fbeOffset, 4)
  let serialized = writer.serialize(struct1)
  t.equal(serialized, writer.buffer.size)
  t.true(writer.verify())
  writer.next(serialized)
  t.equal(writer.model.fbeOffset, (4 + writer.buffer.size))

  // Check the serialized FBE size
  t.equal(writer.buffer.size, 1290)

  // Deserialize the struct from the FBE stream
  let struct2 = new prototest.StructArray()
  let reader = new prototest.StructArrayModel(new fbe.ReadBuffer())
  t.equal(reader.model.fbeType, 125)
  t.equal(reader.model.fbeOffset, 4)
  reader.attachBuffer(writer.buffer)
  t.true(reader.verify())
  let deserialized = reader.deserialize(struct2)
  t.true(deserialized.size, reader.buffer.size)
  reader.next(deserialized.size)
  t.true(reader.model.fbeOffset, (4 + reader.buffer.size))

  t.equal(struct2.f1.length, 2)
  t.equal(struct2.f1[0], 48)
  t.equal(struct2.f1[1], 65)
  t.equal(struct2.f2.length, 2)
  t.equal(struct2.f2[0], 97)
  t.equal(struct2.f2[1], undefined)
  t.equal(struct2.f3.length, 2)
  t.equal(struct2.f3[0].length, 3)
  t.equal(struct2.f3[0][0], 48)
  t.equal(struct2.f3[0][1], 48)
  t.equal(struct2.f3[0][2], 48)
  t.equal(struct2.f3[1].length, 3)
  t.equal(struct2.f3[1][0], 65)
  t.equal(struct2.f3[1][1], 65)
  t.equal(struct2.f3[1][2], 65)
  t.equal(struct2.f4.length, 2)
  t.notEqual(struct2.f4[0], undefined)
  t.equal(struct2.f4[0].length, 3)
  t.equal(struct2.f4[0][0], 97)
  t.equal(struct2.f4[0][1], 97)
  t.equal(struct2.f4[0][2], 97)
  t.equal(struct2.f4[1], undefined)
  t.equal(struct2.f5.length, 2)
  t.true(struct2.f5[0].eq(prototest.EnumSimple.ENUM_VALUE_1))
  t.true(struct2.f5[1].eq(prototest.EnumSimple.ENUM_VALUE_2))
  t.equal(struct2.f6.length, 2)
  t.true(struct2.f6[0].eq(prototest.EnumSimple.ENUM_VALUE_1))
  t.equal(struct2.f6[1], undefined)
  t.equal(struct2.f7.length, 2)
  t.true(struct2.f7[0].hasFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2))
  t.true(struct2.f7[1].hasFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2 | prototest.FlagsSimple.FLAG_VALUE_3))
  t.equal(struct2.f8.length, 2)
  t.true(struct2.f8[0].hasFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2))
  t.equal(struct2.f8[1], undefined)
  t.equal(struct2.f9.length, 2)
  t.equal(struct2.f9[0].f2, true)
  t.equal(struct2.f9[0].f12, 0xFF)
  t.equal(struct2.f9[0].f32, 'Initial string!')
  t.equal(struct2.f9[1].f2, true)
  t.equal(struct2.f9[1].f12, 0xFF)
  t.equal(struct2.f9[1].f32, 'Initial string!')
  t.equal(struct2.f10.length, 2)
  t.notEqual(struct2.f10[0], undefined)
  t.equal(struct2.f10[0].f2, true)
  t.equal(struct2.f10[0].f12, 0xFF)
  t.equal(struct2.f10[0].f32, 'Initial string!')
  t.equal(struct2.f10[1], undefined)
  t.end()
})

test('Serialization: struct vector', function (t) {
  // Create a new struct
  let struct1 = new prototest.StructVector()
  struct1.f1[0] = 48
  struct1.f1[1] = 65
  struct1.f2[0] = 97
  struct1.f2[1] = undefined
  struct1.f3[0] = Array.from('000', x => x.charCodeAt(0))
  struct1.f3[1] = Array.from('AAA', x => x.charCodeAt(0))
  struct1.f4[0] = Array.from('aaa', x => x.charCodeAt(0))
  struct1.f4[1] = undefined
  struct1.f5[0] = prototest.EnumSimple.ENUM_VALUE_1
  struct1.f5[1] = prototest.EnumSimple.ENUM_VALUE_2
  struct1.f6[0] = prototest.EnumSimple.ENUM_VALUE_1
  struct1.f6[1] = undefined
  struct1.f7[0] = prototest.FlagsSimple.fromFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2)
  struct1.f7[1] = prototest.FlagsSimple.fromFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2 | prototest.FlagsSimple.FLAG_VALUE_3)
  struct1.f8[0] = prototest.FlagsSimple.fromFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2)
  struct1.f8[1] = undefined
  struct1.f9[0] = new prototest.StructSimple()
  struct1.f9[1] = new prototest.StructSimple()
  struct1.f10[0] = new prototest.StructSimple()
  struct1.f10[1] = undefined

  // Serialize the struct to the FBE stream
  let writer = new prototest.StructVectorModel(new fbe.WriteBuffer())
  t.equal(writer.model.fbeType, 130)
  t.equal(writer.model.fbeOffset, 4)
  let serialized = writer.serialize(struct1)
  t.equal(serialized, writer.buffer.size)
  t.true(writer.verify())
  writer.next(serialized)
  t.equal(writer.model.fbeOffset, (4 + writer.buffer.size))

  // Check the serialized FBE size
  t.equal(writer.buffer.size, 1370)

  // Deserialize the struct from the FBE stream
  let struct2 = new prototest.StructVector()
  let reader = new prototest.StructVectorModel(new fbe.ReadBuffer())
  t.equal(reader.model.fbeType, 130)
  t.equal(reader.model.fbeOffset, 4)
  reader.attachBuffer(writer.buffer)
  t.true(reader.verify())
  let deserialized = reader.deserialize(struct2)
  t.true(deserialized.size, reader.buffer.size)
  reader.next(deserialized.size)
  t.true(reader.model.fbeOffset, (4 + reader.buffer.size))

  t.equal(struct2.f1.length, 2)
  t.equal(struct2.f1[0], 48)
  t.equal(struct2.f1[1], 65)
  t.equal(struct2.f2.length, 2)
  t.equal(struct2.f2[0], 97)
  t.equal(struct2.f2[1], undefined)
  t.equal(struct2.f3.length, 2)
  t.equal(struct2.f3[0].length, 3)
  t.equal(struct2.f3[0][0], 48)
  t.equal(struct2.f3[0][1], 48)
  t.equal(struct2.f3[0][2], 48)
  t.equal(struct2.f3[1].length, 3)
  t.equal(struct2.f3[1][0], 65)
  t.equal(struct2.f3[1][1], 65)
  t.equal(struct2.f3[1][2], 65)
  t.equal(struct2.f4.length, 2)
  t.notEqual(struct2.f4[0], undefined)
  t.equal(struct2.f4[0].length, 3)
  t.equal(struct2.f4[0][0], 97)
  t.equal(struct2.f4[0][1], 97)
  t.equal(struct2.f4[0][2], 97)
  t.equal(struct2.f4[1], undefined)
  t.equal(struct2.f5.length, 2)
  t.true(struct2.f5[0].eq(prototest.EnumSimple.ENUM_VALUE_1))
  t.true(struct2.f5[1].eq(prototest.EnumSimple.ENUM_VALUE_2))
  t.equal(struct2.f6.length, 2)
  t.true(struct2.f6[0].eq(prototest.EnumSimple.ENUM_VALUE_1))
  t.equal(struct2.f6[1], undefined)
  t.equal(struct2.f7.length, 2)
  t.true(struct2.f7[0].hasFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2))
  t.true(struct2.f7[1].hasFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2 | prototest.FlagsSimple.FLAG_VALUE_3))
  t.equal(struct2.f8.length, 2)
  t.true(struct2.f8[0].hasFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2))
  t.equal(struct2.f8[1], undefined)
  t.equal(struct2.f9.length, 2)
  t.equal(struct2.f9[0].f2, true)
  t.equal(struct2.f9[0].f12, 0xFF)
  t.equal(struct2.f9[0].f32, 'Initial string!')
  t.equal(struct2.f9[1].f2, true)
  t.equal(struct2.f9[1].f12, 0xFF)
  t.equal(struct2.f9[1].f32, 'Initial string!')
  t.equal(struct2.f10.length, 2)
  t.notEqual(struct2.f10[0], undefined)
  t.equal(struct2.f10[0].f2, true)
  t.equal(struct2.f10[0].f12, 0xFF)
  t.equal(struct2.f10[0].f32, 'Initial string!')
  t.equal(struct2.f10[1], undefined)
  t.end()
})

test('Serialization: struct list', function (t) {
  // Create a new struct
  let struct1 = new prototest.StructList()
  struct1.f1[0] = 48
  struct1.f1[1] = 65
  struct1.f2[0] = 97
  struct1.f2[1] = undefined
  struct1.f3[0] = Array.from('000', x => x.charCodeAt(0))
  struct1.f3[1] = Array.from('AAA', x => x.charCodeAt(0))
  struct1.f4[0] = Array.from('aaa', x => x.charCodeAt(0))
  struct1.f4[1] = undefined
  struct1.f5[0] = prototest.EnumSimple.ENUM_VALUE_1
  struct1.f5[1] = prototest.EnumSimple.ENUM_VALUE_2
  struct1.f6[0] = prototest.EnumSimple.ENUM_VALUE_1
  struct1.f6[1] = undefined
  struct1.f7[0] = prototest.FlagsSimple.fromFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2)
  struct1.f7[1] = prototest.FlagsSimple.fromFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2 | prototest.FlagsSimple.FLAG_VALUE_3)
  struct1.f8[0] = prototest.FlagsSimple.fromFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2)
  struct1.f8[1] = undefined
  struct1.f9[0] = new prototest.StructSimple()
  struct1.f9[1] = new prototest.StructSimple()
  struct1.f10[0] = new prototest.StructSimple()
  struct1.f10[1] = undefined

  // Serialize the struct to the FBE stream
  let writer = new prototest.StructListModel(new fbe.WriteBuffer())
  t.equal(writer.model.fbeType, 131)
  t.equal(writer.model.fbeOffset, 4)
  let serialized = writer.serialize(struct1)
  t.equal(serialized, writer.buffer.size)
  t.true(writer.verify())
  writer.next(serialized)
  t.equal(writer.model.fbeOffset, (4 + writer.buffer.size))

  // Check the serialized FBE size
  t.equal(writer.buffer.size, 1370)

  // Deserialize the struct from the FBE stream
  let struct2 = new prototest.StructList()
  let reader = new prototest.StructListModel(new fbe.ReadBuffer())
  t.equal(reader.model.fbeType, 131)
  t.equal(reader.model.fbeOffset, 4)
  reader.attachBuffer(writer.buffer)
  t.true(reader.verify())
  let deserialized = reader.deserialize(struct2)
  t.true(deserialized.size, reader.buffer.size)
  reader.next(deserialized.size)
  t.true(reader.model.fbeOffset, (4 + reader.buffer.size))

  t.equal(struct2.f1.length, 2)
  t.equal(struct2.f1[0], 48)
  t.equal(struct2.f1[1], 65)
  t.equal(struct2.f2.length, 2)
  t.equal(struct2.f2[0], 97)
  t.equal(struct2.f2[1], undefined)
  t.equal(struct2.f3.length, 2)
  t.equal(struct2.f3[0].length, 3)
  t.equal(struct2.f3[0][0], 48)
  t.equal(struct2.f3[0][1], 48)
  t.equal(struct2.f3[0][2], 48)
  t.equal(struct2.f3[1].length, 3)
  t.equal(struct2.f3[1][0], 65)
  t.equal(struct2.f3[1][1], 65)
  t.equal(struct2.f3[1][2], 65)
  t.equal(struct2.f4.length, 2)
  t.notEqual(struct2.f4[0], undefined)
  t.equal(struct2.f4[0].length, 3)
  t.equal(struct2.f4[0][0], 97)
  t.equal(struct2.f4[0][1], 97)
  t.equal(struct2.f4[0][2], 97)
  t.equal(struct2.f4[1], undefined)
  t.equal(struct2.f5.length, 2)
  t.true(struct2.f5[0].eq(prototest.EnumSimple.ENUM_VALUE_1))
  t.true(struct2.f5[1].eq(prototest.EnumSimple.ENUM_VALUE_2))
  t.equal(struct2.f6.length, 2)
  t.true(struct2.f6[0].eq(prototest.EnumSimple.ENUM_VALUE_1))
  t.equal(struct2.f6[1], undefined)
  t.equal(struct2.f7.length, 2)
  t.true(struct2.f7[0].hasFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2))
  t.true(struct2.f7[1].hasFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2 | prototest.FlagsSimple.FLAG_VALUE_3))
  t.equal(struct2.f8.length, 2)
  t.true(struct2.f8[0].hasFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2))
  t.equal(struct2.f8[1], undefined)
  t.equal(struct2.f9.length, 2)
  t.equal(struct2.f9[0].f2, true)
  t.equal(struct2.f9[0].f12, 0xFF)
  t.equal(struct2.f9[0].f32, 'Initial string!')
  t.equal(struct2.f9[1].f2, true)
  t.equal(struct2.f9[1].f12, 0xFF)
  t.equal(struct2.f9[1].f32, 'Initial string!')
  t.equal(struct2.f10.length, 2)
  t.notEqual(struct2.f10[0], undefined)
  t.equal(struct2.f10[0].f2, true)
  t.equal(struct2.f10[0].f12, 0xFF)
  t.equal(struct2.f10[0].f32, 'Initial string!')
  t.equal(struct2.f10[1], undefined)
  t.end()
})

test('Serialization: struct set', function (t) {
  // Create a new struct
  let struct1 = new prototest.StructSet()
  struct1.f1.add(48)
  struct1.f1.add(65)
  struct1.f1.add(97)
  struct1.f2.add(prototest.EnumSimple.ENUM_VALUE_1)
  struct1.f2.add(prototest.EnumSimple.ENUM_VALUE_2)
  struct1.f3.add(prototest.FlagsSimple.fromFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2))
  struct1.f3.add(prototest.FlagsSimple.fromFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2 | prototest.FlagsSimple.FLAG_VALUE_3))
  let s1 = new prototest.StructSimple()
  s1.id = 48
  struct1.f4.add(s1)
  let s2 = new prototest.StructSimple()
  s2.id = 65
  struct1.f4.add(s2)

  // Serialize the struct to the FBE stream
  let writer = new prototest.StructSetModel(new fbe.WriteBuffer())
  t.equal(writer.model.fbeType, 132)
  t.equal(writer.model.fbeOffset, 4)
  let serialized = writer.serialize(struct1)
  t.equal(serialized, writer.buffer.size)
  t.true(writer.verify())
  writer.next(serialized)
  t.equal(writer.model.fbeOffset, (4 + writer.buffer.size))

  // Check the serialized FBE size
  t.equal(writer.buffer.size, 843)

  // Deserialize the struct from the FBE stream
  let struct2 = new prototest.StructSet()
  let reader = new prototest.StructSetModel(new fbe.ReadBuffer())
  t.equal(reader.model.fbeType, 132)
  t.equal(reader.model.fbeOffset, 4)
  reader.attachBuffer(writer.buffer)
  t.true(reader.verify())
  let deserialized = reader.deserialize(struct2)
  t.true(deserialized.size, reader.buffer.size)
  reader.next(deserialized.size)
  t.true(reader.model.fbeOffset, (4 + reader.buffer.size))

  let found

  // Javascript sets are limited to check object key with custom key comparator!
  t.equal(struct2.f1.size, 3)
  t.true(struct2.f1.has(48))
  t.true(struct2.f1.has(65))
  t.true(struct2.f1.has(97))
  t.equal(struct2.f2.size, 2)
  found = false
  struct2.f2.forEach(value => { if (value.eq(prototest.EnumSimple.ENUM_VALUE_1)) found = true })
  t.true(found)
  found = false
  struct2.f2.forEach(value => { if (value.eq(prototest.EnumSimple.ENUM_VALUE_2)) found = true })
  t.true(found)
  t.equal(struct2.f3.size, 2)
  found = false
  struct2.f3.forEach(value => { if (value.eq(prototest.FlagsSimple.fromFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2))) found = true })
  t.true(found)
  found = false
  struct2.f3.forEach(value => { if (value.eq(prototest.FlagsSimple.fromFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2 | prototest.FlagsSimple.FLAG_VALUE_3))) found = true })
  t.true(found)
  t.equal(struct2.f4.size, 2)
  found = false
  struct2.f4.forEach(value => { if (value.eq(s1)) found = true })
  t.true(found)
  found = false
  struct2.f4.forEach(value => { if (value.eq(s2)) found = true })
  t.true(found)
  t.end()
})

test('Serialization: struct map', function (t) {
  // Create a new struct
  let struct1 = new prototest.StructMap()
  struct1.f1.set(10, 48)
  struct1.f1.set(20, 65)
  struct1.f2.set(10, 97)
  struct1.f2.set(20, undefined)
  struct1.f3.set(10, Array.from('000', x => x.charCodeAt(0)))
  struct1.f3.set(20, Array.from('AAA', x => x.charCodeAt(0)))
  struct1.f4.set(10, Array.from('aaa', x => x.charCodeAt(0)))
  struct1.f4.set(20, undefined)
  struct1.f5.set(10, prototest.EnumSimple.ENUM_VALUE_1)
  struct1.f5.set(20, prototest.EnumSimple.ENUM_VALUE_2)
  struct1.f6.set(10, prototest.EnumSimple.ENUM_VALUE_1)
  struct1.f6.set(20, undefined)
  struct1.f7.set(10, prototest.FlagsSimple.fromFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2))
  struct1.f7.set(20, prototest.FlagsSimple.fromFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2 | prototest.FlagsSimple.FLAG_VALUE_3))
  struct1.f8.set(10, prototest.FlagsSimple.fromFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2))
  struct1.f8.set(20, undefined)
  let s1 = new prototest.StructSimple()
  s1.id = 48
  struct1.f9.set(10, s1)
  let s2 = new prototest.StructSimple()
  s2.id = 65
  struct1.f9.set(20, s2)
  struct1.f10.set(10, s1)
  struct1.f10.set(20, undefined)

  // Serialize the struct to the FBE stream
  let writer = new prototest.StructMapModel(new fbe.WriteBuffer())
  t.equal(writer.model.fbeType, 140)
  t.equal(writer.model.fbeOffset, 4)
  let serialized = writer.serialize(struct1)
  t.equal(serialized, writer.buffer.size)
  t.true(writer.verify())
  writer.next(serialized)
  t.equal(writer.model.fbeOffset, (4 + writer.buffer.size))

  // Check the serialized FBE size
  t.equal(writer.buffer.size, 1450)

  // Deserialize the struct from the FBE stream
  let struct2 = new prototest.StructMap()
  let reader = new prototest.StructMapModel(new fbe.ReadBuffer())
  t.equal(reader.model.fbeType, 140)
  t.equal(reader.model.fbeOffset, 4)
  reader.attachBuffer(writer.buffer)
  t.true(reader.verify())
  let deserialized = reader.deserialize(struct2)
  t.true(deserialized.size, reader.buffer.size)
  reader.next(deserialized.size)
  t.true(reader.model.fbeOffset, (4 + reader.buffer.size))

  t.equal(struct2.f1.size, 2)
  t.equal(struct2.f1.get(10), 48)
  t.equal(struct2.f1.get(20), 65)
  t.equal(struct2.f2.size, 2)
  t.equal(struct2.f2.get(10), 97)
  t.equal(struct2.f2.get(20), undefined)
  t.equal(struct2.f3.size, 2)
  t.equal(struct2.f3.get(10).length, 3)
  t.equal(struct2.f3.get(20).length, 3)
  t.equal(struct2.f4.size, 2)
  t.equal(struct2.f4.get(10).length, 3)
  t.equal(struct2.f4.get(20), undefined)
  t.equal(struct2.f5.size, 2)
  t.true(struct2.f5.get(10).eq(prototest.EnumSimple.ENUM_VALUE_1))
  t.true(struct2.f5.get(20).eq(prototest.EnumSimple.ENUM_VALUE_2))
  t.equal(struct2.f6.size, 2)
  t.true(struct2.f6.get(10).eq(prototest.EnumSimple.ENUM_VALUE_1))
  t.equal(struct2.f6.get(20), undefined)
  t.equal(struct2.f7.size, 2)
  t.true(struct2.f7.get(10).hasFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2))
  t.true(struct2.f7.get(20).hasFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2 | prototest.FlagsSimple.FLAG_VALUE_3))
  t.equal(struct2.f8.size, 2)
  t.true(struct2.f8.get(10).hasFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2))
  t.equal(struct2.f8.get(20), undefined)
  t.equal(struct2.f9.size, 2)
  t.equal(struct2.f9.get(10).id, 48)
  t.equal(struct2.f9.get(20).id, 65)
  t.equal(struct2.f10.size, 2)
  t.equal(struct2.f10.get(10).id, 48)
  t.equal(struct2.f10.get(20), undefined)
  t.end()
})

test('Serialization: struct hash', function (t) {
  // Create a new struct
  let struct1 = new prototest.StructHash()
  struct1.f1.set('10', 48)
  struct1.f1.set('20', 65)
  struct1.f2.set('10', 97)
  struct1.f2.set('20', undefined)
  struct1.f3.set('10', Array.from('000', x => x.charCodeAt(0)))
  struct1.f3.set('20', Array.from('AAA', x => x.charCodeAt(0)))
  struct1.f4.set('10', Array.from('aaa', x => x.charCodeAt(0)))
  struct1.f4.set('20', undefined)
  struct1.f5.set('10', prototest.EnumSimple.ENUM_VALUE_1)
  struct1.f5.set('20', prototest.EnumSimple.ENUM_VALUE_2)
  struct1.f6.set('10', prototest.EnumSimple.ENUM_VALUE_1)
  struct1.f6.set('20', undefined)
  struct1.f7.set('10', prototest.FlagsSimple.fromFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2))
  struct1.f7.set('20', prototest.FlagsSimple.fromFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2 | prototest.FlagsSimple.FLAG_VALUE_3))
  struct1.f8.set('10', prototest.FlagsSimple.fromFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2))
  struct1.f8.set('20', undefined)
  let s1 = new prototest.StructSimple()
  s1.id = 48
  struct1.f9.set('10', s1)
  let s2 = new prototest.StructSimple()
  s2.id = 65
  struct1.f9.set('20', s2)
  struct1.f10.set('10', s1)
  struct1.f10.set('20', undefined)

  // Serialize the struct to the FBE stream
  let writer = new prototest.StructHashModel(new fbe.WriteBuffer())
  t.equal(writer.model.fbeType, 141)
  t.equal(writer.model.fbeOffset, 4)
  let serialized = writer.serialize(struct1)
  t.equal(serialized, writer.buffer.size)
  t.true(writer.verify())
  writer.next(serialized)
  t.equal(writer.model.fbeOffset, (4 + writer.buffer.size))

  // Check the serialized FBE size
  t.equal(writer.buffer.size, 1570)

  // Deserialize the struct from the FBE stream
  let struct2 = new prototest.StructHash()
  let reader = new prototest.StructHashModel(new fbe.ReadBuffer())
  t.equal(reader.model.fbeType, 141)
  t.equal(reader.model.fbeOffset, 4)
  reader.attachBuffer(writer.buffer)
  t.true(reader.verify())
  let deserialized = reader.deserialize(struct2)
  t.true(deserialized.size, reader.buffer.size)
  reader.next(deserialized.size)
  t.true(reader.model.fbeOffset, (4 + reader.buffer.size))

  t.equal(struct2.f1.size, 2)
  t.equal(struct2.f1.get('10'), 48)
  t.equal(struct2.f1.get('20'), 65)
  t.equal(struct2.f2.size, 2)
  t.equal(struct2.f2.get('10'), 97)
  t.equal(struct2.f2.get('20'), undefined)
  t.equal(struct2.f3.size, 2)
  t.equal(struct2.f3.get('10').length, 3)
  t.equal(struct2.f3.get('20').length, 3)
  t.equal(struct2.f4.size, 2)
  t.equal(struct2.f4.get('10').length, 3)
  t.equal(struct2.f4.get('20'), undefined)
  t.equal(struct2.f5.size, 2)
  t.true(struct2.f5.get('10').eq(prototest.EnumSimple.ENUM_VALUE_1))
  t.true(struct2.f5.get('20').eq(prototest.EnumSimple.ENUM_VALUE_2))
  t.equal(struct2.f6.size, 2)
  t.true(struct2.f6.get('10').eq(prototest.EnumSimple.ENUM_VALUE_1))
  t.equal(struct2.f6.get('20'), undefined)
  t.equal(struct2.f7.size, 2)
  t.true(struct2.f7.get('10').hasFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2))
  t.true(struct2.f7.get('20').hasFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2 | prototest.FlagsSimple.FLAG_VALUE_3))
  t.equal(struct2.f8.size, 2)
  t.true(struct2.f8.get('10').hasFlags(prototest.FlagsSimple.FLAG_VALUE_1 | prototest.FlagsSimple.FLAG_VALUE_2))
  t.equal(struct2.f8.get('20'), undefined)
  t.equal(struct2.f9.size, 2)
  t.equal(struct2.f9.get('10').id, 48)
  t.equal(struct2.f9.get('20').id, 65)
  t.equal(struct2.f10.size, 2)
  t.equal(struct2.f10.get('10').id, 48)
  t.equal(struct2.f10.get('20'), undefined)
  t.end()
})

test('Serialization: struct hash extended', function (t) {
  // Create a new struct
  let struct1 = new prototest.StructHashEx()
  let s1 = new prototest.StructSimple()
  s1.id = 48
  struct1.f1.set(s1, new prototest.StructNested())
  let s2 = new prototest.StructSimple()
  s2.id = 65
  struct1.f1.set(s2, new prototest.StructNested())
  struct1.f2.set(s1, new prototest.StructNested())
  struct1.f2.set(s2, undefined)

  // Serialize the struct to the FBE stream
  let writer = new prototest.StructHashExModel(new fbe.WriteBuffer())
  t.equal(writer.model.fbeType, 142)
  t.equal(writer.model.fbeOffset, 4)
  let serialized = writer.serialize(struct1)
  t.equal(serialized, writer.buffer.size)
  t.true(writer.verify())
  writer.next(serialized)
  t.equal(writer.model.fbeOffset, (4 + writer.buffer.size))

  // Check the serialized FBE size
  t.equal(writer.buffer.size, 7879)

  // Deserialize the struct from the FBE stream
  let struct2 = new prototest.StructHashEx()
  let reader = new prototest.StructHashExModel(new fbe.ReadBuffer())
  t.equal(reader.model.fbeType, 142)
  t.equal(reader.model.fbeOffset, 4)
  reader.attachBuffer(writer.buffer)
  t.true(reader.verify())
  let deserialized = reader.deserialize(struct2)
  t.true(deserialized.size, reader.buffer.size)
  reader.next(deserialized.size)
  t.true(reader.model.fbeOffset, (4 + reader.buffer.size))

  // Javascript maps are limited to get object key with custom key comparator!
  t.equal(struct2.f1.size, 2)
  struct2.f1.forEach((value, key) => { if (key.id === s1.id) t.true(value.f1002.eq(prototest.EnumTyped.ENUM_VALUE_2)) })
  struct2.f1.forEach((value, key) => { if (key.id === s2.id) t.true(value.f1002.eq(prototest.EnumTyped.ENUM_VALUE_2)) })
  t.equal(struct2.f2.size, 2)
  struct2.f2.forEach((value, key) => { if (key.id === s1.id) t.true(value.f1002.eq(prototest.EnumTyped.ENUM_VALUE_2)) })
  struct2.f2.forEach((value, key) => { if (key.id === s2.id) t.equal(value, undefined) })
  t.end()
})
